1
|
|
|
import * as core from '@actions/core'; |
2
|
|
|
import {Octokit} from '@octokit/rest'; |
3
|
|
|
import {config} from 'dotenv'; |
4
|
|
|
import {resolve} from 'path'; |
5
|
|
|
import * as chromeLauncher from 'chrome-launcher'; |
6
|
|
|
import * as lighthouse from 'lighthouse'; |
7
|
|
|
import {Flags} from 'lighthouse/types/externs'; |
8
|
|
|
|
9
|
|
|
// Load environment variables |
10
|
|
|
config({path: resolve(__dirname, '../.env')}); |
11
|
|
|
|
12
|
|
|
interface ActionInputs { |
13
|
|
|
GH_TOKEN: string; |
14
|
|
|
GIST_ID: string; |
15
|
|
|
TEST_URL: string; |
16
|
|
|
PRINT_SUMMARY: boolean; |
17
|
|
|
RESULT_BADGE: boolean; |
18
|
|
|
} |
19
|
|
|
|
20
|
|
|
const ACTION_URL = 'https://github.com/marketplace/actions/lighthouse-box'; |
21
|
|
|
const inputs: ActionInputs = { |
22
|
|
|
GH_TOKEN: core.getInput('GH_TOKEN', {required: true}), |
23
|
|
|
GIST_ID: core.getInput('GIST_ID', {required: true}), |
24
|
|
|
TEST_URL: core.getInput('TEST_URL', {required: true}), |
25
|
|
|
PRINT_SUMMARY: core.getBooleanInput('PRINT_SUMMARY', {required: true}), |
26
|
|
|
RESULT_BADGE: core.getBooleanInput('RESULT_BADGE', {required: true}), |
27
|
|
|
}; |
28
|
|
|
|
29
|
|
|
function getCurrentDate(): string { |
30
|
|
|
return new Date().toLocaleDateString('en-us', {day: 'numeric', year: 'numeric', month: 'short'}); |
31
|
|
|
} |
32
|
|
|
|
33
|
|
|
const UPDATE_DATE: string = getCurrentDate(); |
34
|
|
|
const summaryTable: any[] = []; |
35
|
|
|
const GIST_TITLE = `My website [update ${UPDATE_DATE}]`; |
36
|
|
|
|
37
|
|
|
async function fetchMetrics(testUrl: string): Promise<lighthouse.RunnerResult> { |
38
|
|
|
const chrome = await chromeLauncher.launch({chromeFlags: ['--headless']}); |
39
|
|
|
const OPTIONS: Flags = {logLevel: 'info', output: 'json', port: chrome.port}; |
40
|
|
|
const runnerResult = await lighthouse(testUrl, OPTIONS); |
41
|
|
|
await chrome.kill(); |
42
|
|
|
return runnerResult; |
43
|
|
|
} |
44
|
|
|
|
45
|
|
|
function generateGistContent(runnerResult: lighthouse.RunnerResult): string { |
46
|
|
|
const {categories} = runnerResult.lhr; |
47
|
|
|
const metrics = { |
48
|
|
|
performance: categories.performance.score * 100, |
49
|
|
|
accessibility: categories.accessibility.score * 100, |
50
|
|
|
bestPractices: categories['best-practices'].score * 100, |
51
|
|
|
seo: categories.seo.score * 100, |
52
|
|
|
pwa: categories.pwa.score * 100, |
53
|
|
|
}; |
54
|
|
|
summaryTable.push([{data: 'Category', header: true}, {data: 'Result', header: true}]); |
55
|
|
|
return Object.entries(metrics).map(([category, score]) => { |
56
|
|
|
summaryTable.push([category, `${score}%`]); |
57
|
|
|
let badge = '🙉'; |
58
|
|
|
if (score > 80) badge = '🥈'; |
59
|
|
|
if (score > 90) badge = '🥇'; |
60
|
|
|
if (score === 100) badge = '🏆'; |
61
|
|
|
const title = `${category}:`.padEnd(inputs.RESULT_BADGE ? 37 : 49, '.'); |
62
|
|
|
const percent = `${score}%`.padStart(4, '.'); |
63
|
|
|
const result = inputs.RESULT_BADGE ? ` ${badge}`.padStart(11, '.') : ''; |
64
|
|
|
return `${title}${percent}${result}`; |
65
|
|
|
}).join('\n'); |
66
|
|
|
} |
67
|
|
|
|
68
|
|
|
async function updateGistContent(gistId: string, content: string): Promise<void> { |
69
|
|
|
const octokit = new Octokit({auth: inputs.GH_TOKEN}); |
70
|
|
|
try { |
71
|
|
|
const gist = await octokit.gists.get({gist_id: gistId}); |
72
|
|
|
const filename = Object.keys(gist.data.files || {})[0]; |
73
|
|
|
if (!filename) { |
74
|
|
|
core.setFailed('Action failed: Gist filename not found'); |
75
|
|
|
return; |
76
|
|
|
} |
77
|
|
|
await octokit.gists.update({ |
78
|
|
|
gist_id: gistId, |
79
|
|
|
files: {[filename]: {filename: GIST_TITLE, content}}, |
80
|
|
|
}); |
81
|
|
|
} catch (error: any) { |
82
|
|
|
core.setFailed(`Action failed: Gist ${error.message}`); |
83
|
|
|
} |
84
|
|
|
} |
85
|
|
|
|
86
|
|
|
async function printLighthouseSummary(runnerResult: lighthouse.RunnerResult): Promise<void> { |
87
|
|
|
const summary = core.summary |
88
|
|
|
.addHeading('Results') |
89
|
|
|
.addTable(summaryTable) |
90
|
|
|
.addBreak() |
91
|
|
|
.addRaw('Lighthouse metrics for ') |
92
|
|
|
.addLink(runnerResult.lhr.mainDocumentUrl, runnerResult.lhr.mainDocumentUrl) |
93
|
|
|
.addRaw(' generated by ') |
94
|
|
|
.addLink('lighthouse-box/1.1', ACTION_URL); |
95
|
|
|
if (inputs.PRINT_SUMMARY) { |
96
|
|
|
await summary.write(); |
97
|
|
|
} else { |
98
|
|
|
console.log(summary.stringify()); |
99
|
|
|
} |
100
|
|
|
} |
101
|
|
|
|
102
|
|
|
(async () => { |
103
|
|
|
const runnerResult = await fetchMetrics(inputs.TEST_URL); |
104
|
|
|
const gistContent = generateGistContent(runnerResult); |
105
|
|
|
await updateGistContent(inputs.GIST_ID, gistContent); |
106
|
|
|
await printLighthouseSummary(runnerResult); |
107
|
|
|
})(); |
108
|
|
|
|